package ca.mcgill.schedulability.singleevent.factory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import ca.mcgill.model.digraph.Digraph;
import ca.mcgill.model.digraph.utils.DigraphDisplay;
import ca.mcgill.model.digraph.utils.DigraphGeneration;
import ca.mcgill.model.digraph.utils.PreprocessingUtils;
import ca.mcgill.model.functional.Action;
import ca.mcgill.model.functional.AsynchronousFSMsNetwork;
import ca.mcgill.model.functional.FiniteStateMachine;
import ca.mcgill.model.functional.State;
import ca.mcgill.model.functional.Transition;
import ca.mcgill.model.functional.triggering.ExternalEvent;
import ca.mcgill.model.functional.triggering.InternalEvent;
import ca.mcgill.model.implementation.ImplementationModel;
import ca.mcgill.model.implementation.Task;
import ca.mcgill.schedulability.singleevent.model.SchedulabilityModel;
import ca.mcgill.schedulability.singleevent.model.SingleEventChain;

public class SchedulabilityModelFactory {

	public SchedulabilityModel createSchedulabilityModel() {
		return example1();
	}

	private SchedulabilityModel example1() {

		// we are going to hardcode the chains
		List<SingleEventChain> chains = new ArrayList<SingleEventChain>();
		DigraphDisplay display = new DigraphDisplay();
		
		// creating chain 1

		// Creating FSM1
		// States of FSM1
		State s1_0 = new State("s1,0");
		State s1_1 = new State("s1,1");
		State s1_2 = new State("s1,2");
		// Events
		ExternalEvent e1 = new ExternalEvent("e1", 40);
		InternalEvent int_e1 = new InternalEvent("int_e1");
		InternalEvent int_e2 = new InternalEvent("int_e2");


		// Transitions of FSM1
		Action a1 = new Action("a1", 1, 1);
		Transition gamma1 = new Transition("gamma1", a1, s1_0, s1_1, e1, int_e1);
		Action a2 = new Action("a2", 2, 2);
		Transition gamma2 = new Transition("gamma2", a2, s1_1, s1_2, e1, int_e1);
		Action a3 = new Action("a3", 1, 1);
		Transition gamma3 = new Transition("gamma3", a3, s1_2, s1_0, e1, int_e1);

		List<Transition> trigTrans = new ArrayList<Transition>();
		trigTrans.add(gamma1);
		trigTrans.add(gamma2);
		trigTrans.add(gamma3);

		List<State> states = new ArrayList<State>();
		states.add(s1_0);
		states.add(s1_1);
		states.add(s1_2);

		FiniteStateMachine fsm1 = new FiniteStateMachine("FSM1",trigTrans, states);
		e1.setTriggeredTransitions(trigTrans);

		// States of FSM2
		State s2_0 = new State("s2_0");
		State s2_1 = new State("s2_1");
		State s2_2 = new State("s2_2");
		State s2_3 = new State("s2_3");

		// Transitions of FSM2
		Action a2_0 = new Action("a2_0", 1, 1);
		Transition gamma2_0 = new Transition("gamma2_0", a2_0, s2_0, s2_1,
				int_e1, int_e2);
		Action a2_1 = new Action("a2_1", 3, 3);
		Transition gamma2_1 = new Transition("gamma2_1", a2_1, s2_1, s2_2,
				int_e1, int_e2);
		Action a2_2 = new Action("a2_2", 1, 1);
		Transition gamma2_2 = new Transition("gamma2_2", a2_2, s2_2, s2_3,
				int_e1, int_e2);
		Action a2_3 = new Action("a2_3", 2, 2);
		Transition gamma2_3 = new Transition("gamma2_3", a2_3, s2_3, s2_0,
				int_e1, int_e2);

		trigTrans = new ArrayList<Transition>();
		trigTrans.add(gamma2_0);
		trigTrans.add(gamma2_1);
		trigTrans.add(gamma2_2);
		trigTrans.add(gamma2_3);

		states = new ArrayList<State>();
		states.add(s2_0);
		states.add(s2_1);
		states.add(s2_2);
		states.add(s2_3);

		FiniteStateMachine fsm2 = new FiniteStateMachine("FSM2", trigTrans,
				states);
		int_e1.setTriggeredTransitions(trigTrans);

		// States of FSM3
		State s3_0 = new State("s3_0");
		State s3_1 = new State("s3_1");

		// Transitions of FSM3
		Action a3_0 = new Action("a3_0", 3, 3);
		Transition gamma3_0 = new Transition("gamma3_0", a3_0, s3_0, s3_1,
				int_e2, null);
		Action a3_1 = new Action("a3_1", 1, 1);
		Transition gamma3_1 = new Transition("gamma3_1", a3_1, s3_1, s3_0,
				int_e2, null);

		trigTrans = new ArrayList<Transition>();
		trigTrans.add(gamma3_0);
		trigTrans.add(gamma3_1);

		states = new ArrayList<State>();
		states.add(s3_0);
		states.add(s3_1);

		FiniteStateMachine fsm3 = new FiniteStateMachine("FSM3", trigTrans,
				states);
		int_e2.setTriggeredTransitions(trigTrans);

	
		
		List<FiniteStateMachine> fsms1 = new ArrayList<FiniteStateMachine>();
		fsms1.add(fsm1);
		fsms1.add(fsm2);
		fsms1.add(fsm3);


		AsynchronousFSMsNetwork afsms1 = new AsynchronousFSMsNetwork(fsms1);

		Task task1 = new Task("t1", fsm1, 1);
		Task task2 = new Task("t2", fsm2, 4);
		Task task3 = new Task("t3", fsm3, 5);

		List<Task> tasks1 = new ArrayList<Task>();
		tasks1.add(task1);
		tasks1.add(task2);
		tasks1.add(task3);

		ImplementationModel exampleImpl1 = new ImplementationModel(afsms1, tasks1);
		
		PreprocessingUtils utils1 = new PreprocessingUtils(
				exampleImpl1.getImplementedNetwork());
		
		// generate Digraph for chain 1
		DigraphGeneration digraphGen1 = new DigraphGeneration(utils1);
		Map<Task, Digraph> exampleDigraphMap1 = digraphGen1.generateDigraphs(exampleImpl1);
		List<Digraph> exampleDigraphs1 = new ArrayList<Digraph>();
	
		
		// bug fixed -- NB :  for (Task taskIn : exampleDigraphMap1.keySet()) 
		// doesn't return elements in indexed order, i.e 0, 1, 2 etc
		
		for (Task task : exampleImpl1.getTasks()) {
			//System.out.println("Task: " + task.getName());
			for (Task taskIn : exampleDigraphMap1.keySet()) {
				if (task.equals(taskIn)){
					System.out.println("Task: " + task.getName());
					exampleDigraphs1.add(exampleDigraphMap1.get(task));
					display.printoutDigraphWithWCET(exampleDigraphMap1.get(task));
					System.out.println("\n\n");
				}
			}
		}
	
		// we have one chain
		SingleEventChain chain1 = new SingleEventChain(exampleImpl1,exampleDigraphs1);		
		chains.add(chain1);
	
		
		
		
		
		
		
		// creating chain 2
		// creating events
		ExternalEvent e2 = new ExternalEvent("e2", 30);
		InternalEvent int_e3 = new InternalEvent("int_e3");
		
		// States of FSM4
		State s4_0 = new State("s4_0");
		State s4_1 = new State("s4_1");

		// Transitions of FSM4
		Action a4_0 = new Action("a4_0", 2, 2);
		Transition gamma4_0 = new Transition("gamma4_0", a4_0, s4_0, s4_1, e2,
				int_e3);

		Action a4_1 = new Action("a4_1", 4, 4);
		Transition gamma4_1 = new Transition("gamma4_1", a4_1, s4_1, s4_0, e2,
				int_e3);

		trigTrans = new ArrayList<Transition>();
		trigTrans.add(gamma4_0);
		trigTrans.add(gamma4_1);

		states = new ArrayList<State>();
		states.add(s4_0);
		states.add(s4_1);

		FiniteStateMachine fsm4 = new FiniteStateMachine("FSM4", trigTrans,
				states);
		e2.setTriggeredTransitions(trigTrans);

		// States of FSM5
		State s5_0 = new State("s5_0");
		State s5_1 = new State("s5_1");
		State s5_2 = new State("s5_2");

		// Transitions of FSM5
		Action a5_0 = new Action("a5_0", 1, 1);
		Transition gamma5_0 = new Transition("gamma5_0", a5_0, s5_0, s5_1,
				int_e3, null);

		Action a5_1 = new Action("a5_1", 3, 3);
		Transition gamma5_1 = new Transition("gamma5_1", a5_1, s5_1, s5_2,
				int_e3, null);

		Action a5_2 = new Action("a5_2", 1, 1);
		Transition gamma5_2 = new Transition("gamma5_2", a5_2, s5_2, s5_0,
				int_e3, null);

		trigTrans = new ArrayList<Transition>();
		trigTrans.add(gamma5_0);
		trigTrans.add(gamma5_1);
		trigTrans.add(gamma5_2);

		states = new ArrayList<State>();
		states.add(s5_0);
		states.add(s5_1);
		states.add(s5_2);

		FiniteStateMachine fsm5 = new FiniteStateMachine("FSM5", trigTrans,
				states);
		int_e3.setTriggeredTransitions(trigTrans);
		
		

		List<FiniteStateMachine> fsms2 = new ArrayList<FiniteStateMachine>();
		fsms2.add(fsm4);
		fsms2.add(fsm5);

		AsynchronousFSMsNetwork afsms2 = new AsynchronousFSMsNetwork(fsms2);

		Task task4 = new Task("t4", fsm4, 2);
		Task task5 = new Task("t5", fsm5, 3);
		List<Task> tasks2 = new ArrayList<Task>();
		tasks2.add(task4);
		tasks2.add(task5);

		ImplementationModel exampleImpl2 = new ImplementationModel(afsms2, tasks2);
		
		PreprocessingUtils utils2 = new PreprocessingUtils(
				exampleImpl2.getImplementedNetwork());
		
		// generate Digraph 
		DigraphGeneration digraphGen2 = new DigraphGeneration(utils2);
		Map<Task, Digraph> exampleDigraphMap2 = digraphGen2.generateDigraphs(exampleImpl2);
		List<Digraph> exampleDigraphs2 = new ArrayList<Digraph>();
	

		for (Task task : exampleImpl2.getTasks()) {
			//System.out.println("Task: " + task.getName());
			for (Task taskIn : exampleDigraphMap2.keySet()) {
				if (task.equals(taskIn)){
					System.out.println("Task: " + task.getName());
					exampleDigraphs2.add(exampleDigraphMap2.get(task));
					display.printoutDigraphWithWCET(exampleDigraphMap2.get(task));
					System.out.println("\n\n");
				}
			}
		}

		
		// TODO here add a initialize the DTsets digraphs --> 
		// do this for every FSM cause someone will preempt some 
		// other or not (it depends)
		
		// 1) create something like DTsets.generateDTsets(example);
		// 2) do the same as digraphs, so have one DTset per task/FSM
		// 3) add them as 3 argument of a chain !!
		
		// we have the second chain
		SingleEventChain chain2 = new SingleEventChain(exampleImpl2,exampleDigraphs2);
		chains.add(chain2);
	
			
		SchedulabilityModel example = new SchedulabilityModel(chains);
		return example;

	}

}
